package commons.base.serialization.protostuff;

import java.io.ByteArrayOutputStream;

import commons.base.util.DeflateUtil;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

/**
 * 二进制序列化与反序列化工具
 * 
 * @author yuan<cihang.yuan@happyelements.com>
 * @since 1.7
 */
public class Protostuff {

	/** 超过这个字节时就压缩 */
	private static final int COMPRESS_NEED = 100;
	/** 数据未压缩标识 */
	private static final byte NO_COMPRESS_FLAG = 0x00;
	/** 数据压缩标识 */
	private static final byte COMPRESS_FLAG = 0x01;
	
	public static <T> byte[] serial(T obj){
		return serial(obj, true);
	}
	public static <T> byte[] serialNoCompress(T obj){
		return serial(obj, false);
	}
	@SuppressWarnings("unchecked")
	private static <T> byte[] serial(T obj, boolean supportCompress) {
		Class<T> clazz = (Class<T>) obj.getClass();
		Schema<T> schema = RuntimeSchema.getSchema(clazz);
		// 缓存buff
		LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		// 序列化成protobuf的二进制数据
		byte[] data = null;
		try {
			data = ProtobufIOUtil.toByteArray(obj, schema, buffer);
		} finally {
			buffer.clear();
		}
		
		if(!supportCompress){
			return data;
		}

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		if(data.length >= COMPRESS_NEED){
			baos.write(COMPRESS_FLAG);
			DeflateUtil.compress(data, 0, data.length, baos);
		}else{
			baos.write(NO_COMPRESS_FLAG);
			baos.write(data, 0, data.length);
		}
		
		return baos.toByteArray();
	}

	public static <T> T deserial(byte[] data, Class<T> clazz){
		return deserial(data, clazz, true);
	}
	public static <T> T deserialNoCompress(byte[] data, Class<T> clazz){
		return deserial(data, clazz, false);
	}
	private static <T> T deserial(byte[] data, Class<T> clazz, boolean supportCompress) {
		Schema<T> schema = RuntimeSchema.getSchema(clazz);
		T message = schema.newMessage();
		
		if(!supportCompress){
			ProtobufIOUtil.mergeFrom(data, message, schema);
		}else if(data[0] == COMPRESS_FLAG){
			ProtobufIOUtil.mergeFrom(DeflateUtil.uncompress(data, 1, data.length-1), message, schema);
		}else{
			ProtobufIOUtil.mergeFrom(data, 1, data.length-1, message, schema);
		}
		
		
		return message;
	}

}
